package cn.juforg.gravitysnake;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;

/**
 * @author Juforg
 * @version 1.0
 * @date 2011/08/26
 */

public class SnakeView extends Garden implements SensorEventListener{
	
	/**
	 * 初始化界面
	 * @param context
	 * @param attrs
	 * @param defStyle
	 */
	public SnakeView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// 构造时初始化
		initSnakeView();
	}
	
	public SnakeView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initSnakeView();
	}
	
	public static final int PAUSE = 0;			//游戏的所有状态
	public static final int READY = 1;
	public static final int RUNNING = 2;
	public static final int LOSE = 3;
	
	private int currMode = READY;					//游戏的当钱状态 默认为准备
	
	public static final int TURN_UP = 1;		//游戏的所有方向
	public static final int TURN_DOWN = 2;
	public static final int TURN_LEFT = 3;
	public static final int TURN_RIGHT = 4;
	
	private int currDirection = TURN_UP;		//游戏的当前方向 默认向上
	
	private int nextDirection = TURN_UP;		//游戏的下个方向
	
	
	private static final int SNAKE_STAR = 1;		//游戏中的元素
	private static final int APPLE_STAR = 2;
	private static final int WALL_STAR = 3;
	
	private long score = 0;						//游戏的得分
	
	private long speedDely = 600;				//蛇移动的速度
	
	private long lastMoveTime ;					//最后一次移动的时间
	
	private List<Coordinate> snakeBody = new ArrayList<SnakeView.Coordinate>();	//蛇身数组
	
	private List<Coordinate> appleList = new ArrayList<SnakeView.Coordinate>();	//苹果数组
	
	private static final Random RND = new Random();	//随机数
	
	private RefreshHandler reDrawHandler = new RefreshHandler();			//延时发送消息
	
	/**
	 * 内部类
	 * @author Juforg
	 * 
	 */
	class RefreshHandler extends Handler{

		@Override
		public void handleMessage(Message msg) {
//			super.handleMessage(msg);
			SnakeView.this.updateGame();
			SnakeView.this.invalidate();			// 画面重绘
			Log.i("Gravity Snake", "handleMessage :画面重绘");
		}
		
		//延迟发送
		public void sleep(long delayMillis){
			this.removeMessages(0);
			sendMessageDelayed(obtainMessage(0), delayMillis);
		}
	}
	
	Resources res = this.getContext().getResources();
	//初始化
	private void initSnakeView(){
		this.setFocusable(true);
		
		resetPiece(4);		//重设图像片数组
		
		//把资源图片存入数组
		loadPiece(SNAKE_STAR, res.getDrawable(R.drawable.snakestar));
		loadPiece(APPLE_STAR, res.getDrawable(R.drawable.applestar));
		loadPiece(WALL_STAR, res.getDrawable(R.drawable.wallstar));
	}
	
	public void initNewGame(){
		this.snakeBody.clear();
		this.appleList.clear();
		
		//随机一个蛇的头
		int i = 5;
		snakeBody.add(new Coordinate(i, i));
		snakeBody.add(new Coordinate(i+1, i));
		snakeBody.add(new Coordinate(i+2, i));
		snakeBody.add(new Coordinate(i+3, i));
		snakeBody.add(new Coordinate(i+4, i));

		nextDirection = TURN_DOWN ;
		updateWalls();
		//添加两个随机苹果
		addRandomApple();
		addRandomApple();
		
		//蛇移动的速度
		speedDely = 600;
		//得分
		score = 0 ;
	}
	
	Coordinate newApple;
	boolean foundFlag ;
	boolean collision ;
	private void addRandomApple(){
		newApple = null;
		foundFlag = false;
		int newX = maxX - 2;
		int newY = maxY - 2;
		while (!foundFlag) {	//如果没有找到合适的苹果就一直在循环体内
			newX = 1 + RND.nextInt(6);
			newY = 1 + RND.nextInt(6);
			newApple = new Coordinate(newX, newY);	//新苹果
			
			// 确保新苹果不在蛇身下，先假设没有发生冲突
			collision = false;
			if (snakeBody.contains(newApple)) {
				collision = true;
			}
			if (appleList.contains(newApple)) {		//新苹果不能与原来的苹果重叠
				collision = true;
			}
			foundFlag = !collision;
		}
		if (newApple == null) {
			Log.e(TAG, "Somehow ended up with a null newCoord!");
		}
		Log.i(TAG, "newApple:"+newApple.toString());
		appleList.add(newApple);
	}
	
	/**
	 * 把Arraylist 转换为数组
	 * @param cvec
	 * @return 把坐标以[x1,y1,x2,y2,x3,y3....]的形式保存到数组中
	 */
	private int[] coordArrayListToArray(List<Coordinate> cvec){
		int count = cvec.size();
		int[] rawArray = new int[count * 2];
		Coordinate c ;
		for (int i = 0; i < count; i++) {
			c = cvec.get(i);
			rawArray[2 * i] = c.x;
			rawArray[2 * i + 1] = c.y;
		}
		return rawArray;
	}
	
	//保存游戏状态
	public Bundle saveStatus(){
		Bundle bundle = new Bundle();
		bundle.putIntArray("appleList", coordArrayListToArray(appleList));
		bundle.putInt("currDirection", currDirection);
		bundle.putInt("nextDirection", nextDirection);
		bundle.putLong("speedDely", speedDely);
		bundle.putLong("score", score);
		bundle.putIntArray("snakeBody", coordArrayListToArray(snakeBody));
		return bundle;
	}
	/**
	 * 执行与coordArrayListToArray相反的操作
	 * @param rawArray
	 * 		: [x1,y1,x2,y2,...]
	 * @return List<Coordinate>
	 */
	private List<Coordinate>  coordArrayToList(int[] rawArray){
		List<Coordinate> coordArrayList = new ArrayList<SnakeView.Coordinate>();
		
		int count = rawArray.length;
		Coordinate c;
		for (int i = 0; i < count; i += 2) {
			c = new Coordinate(rawArray[i], rawArray[i+1]);
			coordArrayList.add(c);
		}
		
		return coordArrayList;
	}
	
	//恢复游戏状态
	public void restoreStatus(Bundle bundle){
		setMode(PAUSE);
		
		appleList = coordArrayToList(bundle.getIntArray("appleList"));
		snakeBody = coordArrayToList(bundle.getIntArray("snakeBody"));
		currDirection = bundle.getInt("currDirection");
		nextDirection = bundle.getInt("nextDirection");
		speedDely = bundle.getInt("speedDely");
		score = bundle.getInt("score");
	}
	
	
	/**
	 * 监听用户键盘操作，并处理这些操作
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		//向上
		if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
			if (currMode == PAUSE) {
				setMode(RUNNING);
				updateGame();
				return (true);
			}else if(currMode == READY || currMode == LOSE){
				initNewGame();
				setMode(RUNNING);
				updateGame();
				return (true);
			}
			// 如果是运行状态时，如果原方向不是向下，那么方向转向上
			if (currDirection != TURN_DOWN) {
				nextDirection = TURN_UP;
			}
		}
		
		//向下
		if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
			if (currDirection != TURN_UP) {
				nextDirection = TURN_DOWN;
			}
			return (true);
		}
		
		//向左
		if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
			if (currDirection != TURN_RIGHT) {
				nextDirection = TURN_LEFT;
			}
			return (true);
		}
		
		//向右
		if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
			if (currDirection != TURN_LEFT) {
				nextDirection = TURN_RIGHT;
			}
		}
		return super.onKeyDown(keyCode, event);
	}

	// 设置游戏状态
	public void setMode(int newMode){
//		int oldMode = currMode;
		currMode = newMode;
		
		//如果新状态是运行状态，且原有状态为不运行，那么就开始游戏
		if (currMode == RUNNING ) {
			updateGame();
			return;
		}
		
//		CharSequence str = "";

		if (newMode == PAUSE) {
			new  AlertDialog.Builder(getContext())
			.setTitle(R.string.mode_pause)
			.setMessage(res.getText(R.string.mode_pause))  
			.setPositiveButton(R.string.continue_game ,
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int which) {
							dialog.dismiss();
							}
						}
			        )
	        .setNegativeButton(R.string.exit_game , 
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int which) {
//							((Activity) getContext().getApplicationContext()).finish();
							dialog.dismiss();
					}
				}
			)
			.show();
		}else if (newMode == LOSE) {
			new  AlertDialog.Builder(getContext())
			.setTitle(R.string.mode_lose_prefix)
			.setMessage(res.getText(R.string.mode_lose_prefix))  
			.setPositiveButton(R.string.restart_game ,
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int which) {
							initNewGame();
							dialog.dismiss();
							}
						}
			        )
	        .setNegativeButton(R.string.exit_game , 
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int which) {
//							((Activity) getContext().getApplicationContext()).finish();
							dialog.dismiss();
					}
				}
			)
			.show();
		}
	}
	
	/**
	 * 更新 游戏的所有元素
	 */
	public void updateGame(){
		if (currMode == RUNNING) {
			long now = System.currentTimeMillis();
			if (now - lastMoveTime>speedDely) {
				Log.i("Gravity Snake", "updateGame :"+now);
				initGardenGrid();
				updateWalls();
				updateSnake();
				updateApples();
				
				lastMoveTime = now;
				//延迟一个时间单位
				reDrawHandler.sleep(speedDely);
			}
		}
	}
	
	//跟新苹果
	private void updateApples(){
		for (Coordinate c : appleList) {
			setGrid(APPLE_STAR, c.x, c.y);
		}
	}
	
	/**
	 * 更新 上下左右 4面墙
	 */
	private void updateWalls(){
		for (int x = 0; x < maxX; x++) {//上下两边的墙
			setGrid(WALL_STAR, x, 0);
			setGrid(WALL_STAR, x, maxY - 1);
		}
		for (int y = 0; y < maxY; y++) {//左右两边的墙
			setGrid(WALL_STAR, 0, y);
			setGrid(WALL_STAR, maxX-1, y);
		}
	}
	
	boolean snakeGrowFlag ;
	Coordinate oldhead ;
	private void updateSnake(){
		snakeGrowFlag = false;
		Coordinate newHead = new Coordinate(0, 0);
		oldhead = snakeBody.get(0);
		newHead.x = oldhead.x;
		newHead.y = oldhead.y;
		currDirection = nextDirection;	//蛇头方向转为新方向
		//根据蛇头方向确定下个蛇头坐标
		switch (currDirection) {
		case TURN_UP:
			newHead.y = oldhead.y - 1;
			break;
		case TURN_DOWN:
			newHead.y = oldhead.y + 1;
			break;
		case TURN_LEFT:
			newHead.x = oldhead.x - 1;
			break;
		case TURN_RIGHT:
			newHead.x = oldhead.x + 1;
			break;
		}
		Log.i(TAG, "newHead:" + newHead.toString());
		//检测蛇头是否与墙重叠
		if (newHead.x < 1 || newHead.y <1 || newHead.x > maxX - 2 || newHead.y > maxY - 2) {
			Log.i(TAG, "newHead:"+newHead.toString()+"maxX:"+maxX+"maxY:"+maxY);
			setMode(LOSE);
			Log.i(TAG, "撞墙");
			return;
		}
		//检测蛇头是否与身体重叠
		if (snakeBody.contains(newHead)) {
			setMode(LOSE);
			Log.i(TAG, "蛇头与身体重叠");
			return;
		}
		//检测新蛇头是否与苹果重叠
		if (appleList.contains(newHead)) {
			appleList.remove(newHead);
			addRandomApple();		//增加新苹果
			score++;
			speedDely *= 0.9;		//加速
			snakeGrowFlag = true;
		}
		
		//把蛇头添加进蛇数组
		snakeBody.add(0, newHead);
		//若蛇没有加长的话 就把最后一个删除
		if (!snakeGrowFlag) {
			int last = snakeBody.size()-1;
			snakeBody.remove(last);
		}
		
		//设置新蛇头 和蛇身的颜色
		for (Coordinate c : snakeBody) {
			setGrid(SNAKE_STAR, c.x, c.y);
		}
		setGrid(WALL_STAR, newHead.x, newHead.y);
		
	}
	
	/**
	 * @author Juforg
	 * 坐标类
	 */
	private class Coordinate {
		
		public int x ;
		public int y ;
		
		public Coordinate(int x, int y) {
			this.x = x;
			this.y = y;
		}

		@Override
		public String toString() {
			return "Coordinate:("+this.x+","+this.y+")";
		}

		@Override
		public int hashCode() {
			return Integer.parseInt(""+this.x+this.y);
		}

		@Override
		public boolean equals(Object o) {
			
			return this.hashCode()==o.hashCode();
		}
		
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		// 如果是运行状态时
		if (currMode == RUNNING) {
			float x = event.values[SensorManager.DATA_X];
			float y = event.values[SensorManager.DATA_Y];
			float z = event.values[SensorManager.DATA_Z];
	
			//向上
			if (y < -1) {
				
				if (currDirection != TURN_DOWN) {
					nextDirection = TURN_UP;
				}
			}
			
			//向下
			if (y > 1) {
				if (currDirection != TURN_UP) {
					nextDirection = TURN_DOWN;
				}
			}
			
			//向左
			if (x > 1) {
				if (currDirection != TURN_RIGHT) {
					nextDirection = TURN_LEFT;
				}
			}
			
			//向右
			if (x < -1) {
				if (currDirection != TURN_LEFT) {
					nextDirection = TURN_RIGHT;
				}
			}
		}
	
	}
	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		Log.i(TAG, "snak view onSizeChanged:"+"w:"+w+"h:"+h);
		super.onSizeChanged(w, h, oldw, oldh);
	}
	
	
}
